package game.sprite;

import game.engine.GameController;
import game.physics.GameVector;
import game.physics.ImpulseVector;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collection;


/**
 * This is a sprite that has some way to move each update.
 * 
 * @author Ben Schwab
 * 
 */
public abstract class MovableSprite extends Sprite {

    private GameVector myVelocity;
    private Collection<GameVector> myExternalForces;

    /**
     * Constructor.
     * 
     * @param startLocation Start location of sprite.
     * @param spriteSet Default image string name of sprite.
     * @param isActive If sprite is currently alive on level.
     */
    public MovableSprite (Point startLocation, String spriteSet,
            boolean isActive) {
        super(startLocation, spriteSet, isActive);
        myVelocity = new GameVector(0, 0);
        myExternalForces = new ArrayList<GameVector>();
    }

    /**
     * Move the sprite in some way on the update method.
     */
    protected void move () {
        convertAccelerationVectors();
        setX(getX() + myVelocity.getX());
        setY(getY() + myVelocity.getY());
        applyFriction();
    }

    /**
     * 
     * @return The Sprite's velocity.
     */
    public GameVector getVelocity () {
        return myVelocity;
    }

    /**
     * 
     * @param vel Set the velocity.
     */
    public void setVelocity (GameVector vel) {
        myVelocity = vel;
    }

    /**
     * 
     * @param vectorToAdd Add a force.
     */
    public void addExternalForce (GameVector vectorToAdd) {
        myExternalForces.add(vectorToAdd);
    }

    /**
     * 
     * @param vectorToRemove Force to remove.
     */
    public void removeExternalForce (GameVector vectorToRemove) {
        myExternalForces.remove(vectorToRemove);
    }

    /**
     * 
     * @return Get the forces acting on the sprite.
     */
    public Collection<GameVector> getExternalForces () {
        return myExternalForces;
    }

    /**
     * Convert forces on sprite to a velocity.
     * Removes impulses that have expired.
     */
    public void convertAccelerationVectors () {
        // use a i loop to avoid concurrent modification
        for (int i = myExternalForces.size() - 1; i >= 0; i--) {
            GameVector accelVector = 
                    ((ArrayList<GameVector>) myExternalForces).get(i);
            myVelocity.combineVector(accelVector);
            if (accelVector instanceof ImpulseVector) {
                ImpulseVector impulseVector = (ImpulseVector) accelVector;
                impulseVector.decreaseDuration(1.0 / GameController.GAME_FPS);
                if (!impulseVector.isActive()) {
                    myExternalForces.remove(i);
                    // System.out.println("removing force");
                }

            }
        }
    }

    /**
     * Sprites should have some friction force acting on the velocity or they
     * will
     * accelerate to infinity in some cases.
     */
    public abstract void applyFriction ();

}
